package com.xh.core.util;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.utils.Converters;

public class OpencvUtils {

	static {
		System.out.println(System.getProperty("java.library.path"));
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	/**
	 * 改变对比度
	 * 
	 * @param src
	 * @param out
	 */
	public static void convertTo(String src, String out) {
		Mat mat = Imgcodecs.imread(src);
		Mat new_image = Mat.zeros(mat.size(), mat.type());
		mat.convertTo(new_image, -1, 0.5, 60);
		Imgcodecs.imwrite(out, new_image);
	}

	public static MatOfPoint getMaxContour(String imgPath) {
		// 读取图片
		Mat src = Imgcodecs.imread(imgPath);
		// 边缘检测
		Mat cannyMat = canny(src);
		MatOfPoint maxContour = findMaxContour(cannyMat);
		Point[] ps = maxContour.toArray();
		return maxContour;
	}

	public static Point[] getRectPoints(String imgPath) {
		// 读取图片
		Mat src = Imgcodecs.imread(imgPath);
		// 边缘检测
		Mat cannyMat = canny(src);
		// 获取最大矩形
		RotatedRect rect = findMaxRect(cannyMat);
		Point[] points = new Point[4];
		rect.points(points);
		return points;
	}

	/**
	 * 矫正图像
	 * 
	 * @param imgPath
	 * @return
	 */
	public static Mat correct(String imgPath) {
		// 读取图片
		Mat src = Imgcodecs.imread(imgPath);
		// 边缘检测
		Mat cannyMat = canny(src);
		// 获取最大矩形
		RotatedRect rect = findMaxRect(cannyMat);
		// 旋转矩形
		Mat CorrectImg = rotation(cannyMat, rect);
		Mat NativeCorrectImg = rotation(src, rect);
		// 裁剪矩形
		Mat dst = cutRect(CorrectImg, NativeCorrectImg);
		return dst;
	}

	/**
	 * 矫正图像
	 * 
	 * @param imgPath
	 * @return
	 */
	public static Mat correct(String imgPath, Point[] points) {
		// 读取图片
		Mat src = Imgcodecs.imread(imgPath);
		// 边缘检测
		Mat cannyMat = canny(src);
		// 获取最大矩形
		MatOfPoint2f matOfPoint2f = new MatOfPoint2f(points);
		RotatedRect rect = Imgproc.minAreaRect(matOfPoint2f);

		// RotatedRect rect = findMaxRect(cannyMat);
		// 旋转矩形
		Mat CorrectImg = rotation(cannyMat, rect);
		Mat NativeCorrectImg = rotation(src, rect);
		// 裁剪矩形
		Mat dst = cutRect(CorrectImg, NativeCorrectImg);
		return dst;
	}

	/**
	 * canny算法，边缘检测
	 * 
	 * @param src
	 * @return
	 */
	public static Mat canny(Mat srcImg) {
		if (srcImg.empty()) {
			System.out.println("Please check the path of input image!");
			return null;
		}
		// final int imgRows = srcImg.rows();
		// final int imgCols = srcImg.cols();
		//
		// // Step1: Denoise 降噪
		// Imgproc.GaussianBlur(srcImg, srcImg, new Size(3, 3), 0,
		// Core.BORDER_DEFAULT);
		//
		// // Step2: Convert to gray 灰度化
		// Mat grayImg = Mat.zeros(imgRows, imgCols, CvType.CV_8UC1);
		// if (srcImg.channels() == 3) {
		// Imgproc.cvtColor(srcImg, grayImg, Imgproc.COLOR_BGR2GRAY);
		// }
		// Imgproc.medianBlur(grayImg, grayImg, 3);
		//
		// // Step3: Binary 二值化
		// int maskRoiX = (int) (imgCols / 12.0);
		// int maskRoiY = (int) (imgRows / 8.0);
		// int maskRoiW = (int) (10 / 12.0 * imgCols);
		// int maskRoiH = (int) (6 / 8.0 * imgRows);
		// Rect maskRoi = new Rect(maskRoiX, maskRoiY, maskRoiW, maskRoiH);
		// Mat maskSrc = new Mat(grayImg, maskRoi);
		//
		// Utils utils = new Utils();
		// utils.FindAdaptiveThreshold(maskSrc, 3, 0.80);
		// double thCannyLow = utils.getM_cannyLowTh();
		// double thCannyHigh = utils.getM_cannyHighTh();
		//
		Mat maskImg = srcImg.clone();
		Imgproc.Canny(srcImg, maskImg, 60, 200);
		return maskImg;
	}

	/**
	 * 返回边缘检测之后的最大矩形
	 * 
	 * @param cannyMat
	 *            Canny之后的mat矩阵
	 * @return
	 */
	public static RotatedRect findMaxRect(Mat cannyMat) {
		// 我要从前台获取这个
		MatOfPoint maxContour = findMaxContour(cannyMat);
		MatOfPoint2f matOfPoint2f = new MatOfPoint2f(maxContour.toArray());
		RotatedRect rect = Imgproc.minAreaRect(matOfPoint2f);
		return rect;
	}

	/**
	 * 作用：返回边缘检测之后的最大轮廓
	 * 
	 * @param cannyMat
	 *            Canny之后的Mat矩阵
	 * @return
	 */
	public static MatOfPoint findMaxContour(Mat cannyMat) {
		List<MatOfPoint> contours = findContours(cannyMat);
		return contours.get(contours.size() - 1);
	}

	/**
	 * 寻找轮廓，并按照递增排序
	 * 
	 * @param cannyMat
	 * @return
	 */
	public static List<MatOfPoint> findContours(Mat cannyMat) {
		List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
		Mat hierarchy = new Mat();

		// 寻找轮廓
		Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE,
				new Point(0, 0));

		if (contours.size() <= 0) {
			throw new RuntimeException("未找到图像轮廓");
		} else {
			// 对contours进行了排序，按递增顺序
			contours.sort(new Comparator<MatOfPoint>() {
				@Override
				public int compare(MatOfPoint o1, MatOfPoint o2) {
					MatOfPoint2f mat1 = new MatOfPoint2f(o1.toArray());
					RotatedRect rect1 = Imgproc.minAreaRect(mat1);
					Rect r1 = rect1.boundingRect();

					MatOfPoint2f mat2 = new MatOfPoint2f(o2.toArray());
					RotatedRect rect2 = Imgproc.minAreaRect(mat2);
					Rect r2 = rect2.boundingRect();

					return (int) (r1.area() - r2.area());
				}
			});
			return contours;
		}
	}

	/**
	 * 旋转矩形
	 * 
	 * @param cannyMat
	 *            mat矩阵
	 * @param rect
	 *            矩形
	 * @return
	 */

	public static Mat rotation(Mat cannyMat, RotatedRect rect) {
		// 获取矩形的四个顶点
		Point[] rectPoint = new Point[4];
		rect.points(rectPoint);
		int a = 0;
		if (cannyMat.height() > cannyMat.width()) {
			a = 90;
		}
		double angle = rect.angle + 90 + a;
		Point center = rect.center;
		Mat CorrectImg = new Mat(cannyMat.size(), cannyMat.type());
		cannyMat.copyTo(CorrectImg);
		// 得到旋转矩阵算子
		Mat matrix = Imgproc.getRotationMatrix2D(center, angle, 0.8);
		Imgproc.warpAffine(CorrectImg, CorrectImg, matrix, CorrectImg.size(), 1, 0, new Scalar(0, 0, 0));
		return CorrectImg;
	}

	/**
	 * 把矫正后的图像切割出来
	 * 
	 * @param correctMat
	 *            图像矫正后的Mat矩阵
	 */
	public static Mat cutRect(Mat correctMat, Mat nativeCorrectMat) {
		// 获取最大矩形
		RotatedRect rect = findMaxRect(correctMat);

		Point[] rectPoint = new Point[4];
		rect.points(rectPoint);

		int[] roi = cutRectHelp(rectPoint);

		Mat temp = new Mat(nativeCorrectMat, new Rect(roi[0], roi[1], roi[2], roi[3]));
		Mat t = new Mat();
		temp.copyTo(t);

		// saveImg(t, "C:/Users/admin/Desktop/opencv/open/x/cutRect.jpg");
		return t;
	}

	/**
	 * 把矫正后的图像切割出来--辅助函数(修复)
	 * 
	 * @param rectPoint
	 *            矩形的四个点
	 * @return int[startLeft , startUp , width , height]
	 */
	public static int[] cutRectHelp(Point[] rectPoint) {
		double minX = rectPoint[0].x;
		double maxX = rectPoint[0].x;
		double minY = rectPoint[0].y;
		double maxY = rectPoint[0].y;
		for (int i = 1; i < rectPoint.length; i++) {
			minX = rectPoint[i].x < minX ? rectPoint[i].x : minX;
			maxX = rectPoint[i].x > maxX ? rectPoint[i].x : maxX;
			minY = rectPoint[i].y < minY ? rectPoint[i].y : minY;
			maxY = rectPoint[i].y > maxY ? rectPoint[i].y : maxY;
		}
		int[] roi = { (int) Math.abs(minX), (int) Math.abs(minY), (int) Math.abs(maxX - minX),
				(int) Math.abs(maxY - minY) };
		return roi;
	}

	static void ImageRecify(String src, String out) {
		double degree;
		Mat mat = Imgcodecs.imread(src);
		Mat dst = new Mat();
		// 倾斜角度矫正
		degree = CalcDegree(mat, dst);
		System.out.println(degree);
		rotateImage(mat, dst, degree);
		Mat resulyImage = new Mat(dst, new Rect(0, 0, mat.cols(), mat.rows())); // 根据先验知识，估计好文本的长宽，再裁剪下来
		Imgcodecs.imwrite(out, resulyImage);
	}

	public static double CalcDegree(Mat srcImage, Mat dst) {
		Mat midImage = new Mat(), dstImage = new Mat();
		Imgproc.Canny(srcImage, midImage, 50, 200, 3, false);
		Imgproc.cvtColor(midImage, dstImage, Imgproc.COLOR_GRAY2BGR);

		// 通过霍夫变换检测直线
		Mat lines = srcImage.clone();
		Imgproc.HoughLines(midImage, lines, 1, Math.PI / 180, 250);// 第5个参数就是阈值，阈值越大，检测精度越高
		// cout << lines.size() << endl;
		// 由于图像不同，阈值不好设定，因为阈值设定过高导致无法检测直线，阈值过低直线太多，速度很慢
		// 所以根据阈值由大到小设置了三个阈值，如果经过大量试验后，可以固定一个适合的阈值。

		float sum = 0;
		// 依次画出每条线段
		for (int i = 0; i < lines.rows(); i++) {
			double[] data = lines.get(i, 0);
			double rho = data[0];
			double theta = data[1];
			Point pt1 = new Point(), pt2 = new Point();
			// cout << theta << endl;
			double a = Math.cos(theta), b = Math.sin(theta);
			double x0 = a * rho, y0 = b * rho;
			pt1.x = Math.round(x0 + 1000 * (-b));
			pt1.y = Math.round(y0 + 1000 * (a));
			pt2.x = Math.round(x0 - 1000 * (-b));
			pt2.y = Math.round(y0 - 1000 * (a));
			// 只选角度最小的作为旋转角度
			sum += theta;

			Imgproc.line(dstImage, pt1, pt2, new Scalar(55, 100, 195), 1, 2, Imgproc.LINE_AA); // Scalar函数用于调节线段颜色

			// HighGui.imshow("直线探测效果图", dstImage);
			// HighGui.waitKey(1000);
		}
		float average = sum / lines.rows(); // 对所有角度求平均，这样做旋转效果会更好

		double angle = DegreeTrans(average) - 90;
		rotateImage(dstImage, dst, angle);
		return angle;
	}

	// 度数转换
	static double DegreeTrans(double theta) {
		double res = theta / Math.PI * 180;
		return res;
	}

	static void rotateImage(Mat src, Mat img_rotate, double degree) {
		// 旋转中心为图像中心
		Point center = new Point();
		center.x = src.cols() / 2.0;
		center.y = src.rows() / 2.0;
		double length = 0;
		length = Math.sqrt(src.cols() * src.cols() + src.rows() * src.rows());
		// 计算二维旋转的仿射变换矩阵
		Mat M = Imgproc.getRotationMatrix2D(center, degree, 1);
		Imgproc.warpAffine(src, img_rotate, M, new Size(length, length), 1, 0, new Scalar(255, 255, 255));// 仿射变换，背景色填充为白色
		// Imgcodecs.imwrite("D:/img/5-g.png", img_rotate);
	}

	public static double getAngle(double x1, double y1, double x2, double y2) {
		double x = Math.abs(x1 - x2);
		double y = Math.abs(y1 - y2);
		double z = Math.sqrt(x * x + y * y);
		double angle = Math.asin(y / z) / Math.PI * 180 - 90;// 最终角度
		return angle;
	}
	
	public static void imgThreshold(String src, String out) {
		// 读取图片
		// 加载时灰度
		Mat mat = Imgcodecs.imread(src, Imgcodecs.IMREAD_GRAYSCALE);
		// 保存灰度
		Mat target = new Mat();
		Mat dst = new Mat();
		Imgproc.threshold(mat, target, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);// 灰度图像二值化
		// // 保存二值化后图片
		// Imgproc.adaptiveThreshold(mat, target,
		// 255,Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 25, 15);
		Imgproc.GaussianBlur(target, dst, new Size(1, 1), 0, 0, Core.BORDER_DEFAULT);
		// // 保存二值化后图片
		Imgcodecs.imwrite(out, dst);
	}

	/**
	 * 均值滤波
	 */
	public static void blur(String imgSrc, String imgOut) {

		// 加载时灰度
		Mat src = Imgcodecs.imread(imgSrc);
		Mat dst = src.clone();
		Imgproc.blur(src, dst, new Size(1, 1), new Point(-1, -1), Core.BORDER_DEFAULT);
		Imgcodecs.imwrite(imgOut, dst);

	}

	/**
	 * 高斯滤波
	 */
	public static void GaussianBlur(String imgSrc, String imgOut) {
		// 加载时灰度
		Mat src = Imgcodecs.imread(imgSrc);
		Mat dst = src.clone();
		Imgproc.GaussianBlur(src, dst, new Size(1, 1), 0, 0, Core.BORDER_DEFAULT);
		Imgcodecs.imwrite(imgOut, dst);
	}

	/**
	 * 中值滤波
	 */
	public static void medianBlur(String imgSrc, String imgOut) {
		Mat src = Imgcodecs.imread(imgSrc);
		Mat dst = src.clone();
		Imgproc.medianBlur(src, dst, 5);
		Imgcodecs.imwrite(imgOut, dst);
	}

	public static void removeAllColor(String src, String out){
		Mat mat = Imgcodecs.imread(src);
		Mat hsvMat = new Mat();
		Imgproc.cvtColor(mat,hsvMat,Imgproc.COLOR_BGR2HSV);
		for(int i=0;i<hsvMat.height();i++){
			for(int j=0;j<hsvMat.width();j++){
				double[] datas = hsvMat.get(i, j);
				if(!(-1<datas[0]&&datas[0]<210 && -1<datas[1]&&datas[1]<255 && -1<datas[2]&&datas[2]<80)){
					datas[0] = 0;
					datas[1] = 0;
					datas[2] = 0;
					hsvMat.put(i, j, datas);
				}
			}
		}
		Mat bgrMat = new Mat(), dst = new Mat();
		Imgproc.cvtColor(hsvMat,bgrMat,Imgproc.COLOR_HSV2BGR);
		Imgproc.threshold(bgrMat, dst, 10, 255, Imgproc.THRESH_BINARY_INV);// 灰度图像二值化
		Imgcodecs.imwrite(out,dst);
	}
	
}
